import json

import socks
import socket
import threading
import time
import struct

from Crypto.Cipher import ChaCha20
from google.protobuf import json_format

import Global
from Script.HandleMsg import HandleMsg
from Script.ProtocolMapper import get_protocol_id_by_class


class GameTx:
    def __init__(self,game_index=-1,chacha_key=b'',chacha_nonce=b''):
        self.sock = None
        self.connected = False
        self.recv_thread = None
        self.running = False
        self.seq = 1
        self.recv_buffer = bytearray()  # 使用bytearray代替bytes，提高性能
        self.MAX_PACKET_SIZE = 1024 * 1024  # 最大包大小1MB
        self.HEADER_LENGTH = 4  # 包头长度(字节)，用于存储消息长度
        self.byte_order = 'big'  #little  big

        self.on_disconnect_callback = None
        self.game_index = game_index
        self.recv_block_buffer =b''
        self.chacha_key = chacha_key #
        self.chacha_nonce = chacha_nonce
        self.game_fun = Global.G_Game[self.game_index].GameFun
        self.game = Global.G_Game[self.game_index]
    def __enter__(self):
        """上下文管理器入口方法"""
        return self
    
    def __exit__(self, exc_type, exc_val, exc_tb):
        """上下文管理器退出方法，自动清理资源
        
        Args:
            exc_type: 异常类型
            exc_val: 异常值
            exc_tb: 异常追踪信息
            
        Returns:
            None: 不抑制异常
        """
        self.Close()
        return False  # 不抑制异常，让异常正常传播
    def Connect(self, ip, port, proxy=None):
        """
        连接到指定服务器
        
        Args:
            ip: 服务器IP地址
            port: 服务器端口
            proxy: 代理设置，格式为字典 {'type': 'socks5', 'addr': '127.0.0.1', 'port': 1080, 'username': '', 'password': ''}
            
        Returns:
            bool: 连接是否成功
        """
        try:
            # 创建套接字
            self.sock = socks.socksocket()
            
            # 配置代理
            if proxy:
                proxy_type = proxy.get('type', 'socket5')
                proxy_addr = proxy.get('ip', '')
                proxy_port = proxy.get('port', 0)
                proxy_username = proxy.get('user', '')
                proxy_password = proxy.get('pass', '')
                if len(proxy_addr) >0 and proxy_port >0: #代理信息异常则不进行代理
                    if proxy_type.lower() == 'socket5':
                        self.sock.set_proxy(socks.SOCKS5, proxy_addr, proxy_port,username=proxy_username, password=proxy_password)
                    elif proxy_type.lower() == 'socket4':
                        self.sock.set_proxy(socks.SOCKS4, proxy_addr, proxy_port)
                    elif proxy_type.lower() == 'http':
                        self.sock.set_proxy(socks.HTTP, proxy_addr, proxy_port)

            self.ip = ip
            self.port = port
            # 连接到服务器
            self.sock.connect((ip, port))
            self.connected = True
            self.running = True
            
            # 启动接收线程
            self.recv_thread = threading.Thread(target=self._receive_loop,daemon = True)
            self.recv_thread.start()
            return True
        except Exception as e:
            self.Close()
            return False

    def Close(self):
        """
        关闭连接并清理资源
        """
        self.running = False
        self.connected = False
        
        if self.sock:
            try:
                self.sock.close()
            except Exception as e:
                pass
            self.sock = None
            
        if self.recv_thread and threading.current_thread() != self.recv_thread:
            self.recv_thread.join(timeout=1.0)
            self.recv_thread = None
            
        # 触发断开连接回调
        if self.on_disconnect_callback:
            try:
                self.on_disconnect_callback()
            except Exception as e:
                pass

    def IsConnect(self):
        """
        检查是否已连接
        
        Returns:
            bool: 是否已连接
        """
        return self.connected
    def SetCallbacks(self, on_disconnect=None):
        """
        设置回调函数
        
        Args:
            on_disconnect: 断开连接回调
        """
        self.on_disconnect_callback = on_disconnect

    def Send(self, data):
        """
        发送数据
        
        Args:
            data: 要发送的数据
            
        Returns:
            bool: 发送是否成功
        """
        if not self.connected or not self.sock:
            return False
            
        try:
            self.sock.sendall(data)
            return True
        except Exception as e:
            self.Close()
            return False

    def _receive_loop(self):
        """
        接收数据循环
        """
        while self.running:
            try:
                if not self.connected or not self.sock:
                    break
                    
                # 使用peek方式窥探数据，不从接收缓冲区移除数据
                peek_data = self.sock.recv(self.HEADER_LENGTH, socket.MSG_PEEK)
                if peek_data and (len(peek_data)) >= self.HEADER_LENGTH:
                    # 读取包头获取包长度
                    #packet_length = struct.unpack('<H', peek_data[:self.HEADER_LENGTH])[0]
                    packet_length = int.from_bytes(peek_data[:self.HEADER_LENGTH],self.byte_order)
                    # 检查包长度是否合法
                    if packet_length > self.MAX_PACKET_SIZE:
                        break

                    full_packet_len= packet_length + self.HEADER_LENGTH
                    # 窥探完整的数据包
                    full_peek_data = self.sock.recv(full_packet_len, socket.MSG_PEEK)
                    if len(full_peek_data) >= full_packet_len:
                        # 确认有完整的数据包后，真正读取数据
                        data = self.sock.recv(full_packet_len)

                        if data:
                            # 直接处理数据包，不需要缓冲区
                            self.HandlePacket(data)
            except Exception as e:
                if e.args and e.args[0]!=10053 and e.args[0]!=10054:
                    Global.LOGE(self.game_index,f"[异常]->接收数据异常:{e}")
                self.Close()
                break
            time.sleep(0.01)  # 短暂休眠，避免CPU占用过高
        self.connected = False
        if Global.G_EnableLog:
            Global.G_Game[self.game_index].Log.error(f"[通讯]->断开通讯,IP:{self.ip}:{self.port}")
    # def OnReceive(self, data):
    #     """接收数据的回调函数，处理TCP粘包问题"""
    #     try:
    #         self.recv_buffer.extend(data)  # 使用extend而不是+=，更高效
    #         while True:
    #             buffer_length = len(self.recv_buffer)
    #             if buffer_length < self.HEADER_LENGTH:
    #                 # 数据不足以读取包头
    #                 break
    #
    #             # 读取包头(2字节)获取包长度
    #             packet_length = struct.unpack('>H', self.recv_buffer[:self.HEADER_LENGTH])[0]
    #             # 检查包长度是否合法
    #             if packet_length <= 0 or packet_length > self.MAX_PACKET_SIZE:
    #                 self.recv_buffer.clear()
    #                 break
    #
    #             # 检查缓冲区是否有足够的数据
    #             if buffer_length < self.HEADER_LENGTH + packet_length:
    #                 # 数据不足以读取完整包
    #                 break
    #
    #             # 提取有效载荷数据
    #             packet_data = bytes(self.recv_buffer[self.HEADER_LENGTH:self.HEADER_LENGTH + packet_length])
    #
    #             # 处理有效载荷
    #             self.HandlePacket(packet_length, packet_data)
    #
    #             # 更新缓冲区，移除已处理的数据
    #             del self.recv_buffer[:self.HEADER_LENGTH + packet_length]
    #
    #     except Exception as e:
    #         self.recv_buffer.clear()  # 清空缓冲区
    
    def HandlePacket(self, data):
        try:
            packetLen = int.from_bytes(data[:4], byteorder='big')
            packetid = int.from_bytes(data[4:8], byteorder='big')
            encry_protoData = data[8:packetLen + 4]
            chiper = ChaCha20.new(key=self.chacha_key, nonce=self.chacha_nonce)
            protoData = chiper.decrypt(encry_protoData)
            HandleMsg.HandleGameRecvMsg(self.game_index, packetid, protoData)
        except Exception as e:
            Global.LOGE(self.game_index,f"[异常]->HandlePacket:{e},数据:{data.hex()}")
    def SendPacket(self, data_cls):
        """发送数据包，自动添加包头
        
        Args:
            data: 要发送的数据
            
        Returns:
            bool: 发送是否成功
        """
        if not self.connected or not self.sock:
            return False
        packetId = 0
        try:
            packetId = get_protocol_id_by_class(type(data_cls))
            chiper = ChaCha20.new(key=self.chacha_key, nonce=self.chacha_nonce)
            data = data_cls.SerializeToString()
            encry_data = chiper.decrypt(data)
            send_data =int.to_bytes(len(encry_data)+4,length=4,byteorder='big') + int.to_bytes(packetId,length=4,byteorder='big')+encry_data
            if packetId not in [102]:
                if Global.G_EnableLog:
                    self.game.Log.info(f"send->功能号:{packetId},名称:{data_cls.__class__.__module__.split('.')[-1]}.{data_cls.__class__.__name__},内容:{json_format.MessageToJson(data_cls, indent=None)}")
            is_send =self.Send(send_data)
            return is_send
        except Exception as e:
            if Global.G_EnableLog and self.game_index > -1:
                Global.LOGE(self.game_index,f"[异常]->发送数据异常:{repr(e)},packetId:{packetId},data:{send_data.hex()}")
            return False